(ns ru.nsu.fit.g7202.infix-ops)

(def standard-ops 
  [{'or 'or}
   {'and 'and}
   {'= '=,
    '< '<
    '> '>
    '>= '>=
    '<= '<=
    '!= 'not=}
   {'+ '+,
    '- '-}
   {'mul '*,
    '/ '/
    'mod 'mod},
   {'** 'Math/pow}])


(defn mapm [f map-coll]
    (zipmap (map f (keys map-coll)) (map f (vals map-coll))))

(defn maps [f set-coll]
    (apply hash-set (map f set-coll)))


(declare infix-list-process)

(defn- infix-form-process [all-ops form]
  (cond
    (vector? form) 
    (mapv (partial infix-form-process all-ops) form)
    
    (map? form)
    (mapm (partial infix-form-process all-ops) form)
    
    (set? form)
    (maps (partial infix-form-process all-ops) form)
    
    (list? form)
    (infix-list-process all-ops all-ops form)
    
    :default
    form))

(defn- infix-list-process 
    [all-ops current-ops list-form]
  (let [[ops & rest-ops] current-ops]
    (cond 
      (= (first list-form) 'with-prefix-ops) 
        (cons 'do (rest list-form))

      (not (empty? ops))
        ; Left associative
        #_(let [[inv-tail-form [op & inv-head-form]] (split-with 
                                                     (comp not (partial contains? ops))   
                                                     (reverse list-form))
              head-form (reverse inv-head-form)
              tail-form (reverse inv-tail-form)]
          (if (nil? op)
            (recur all-ops rest-ops tail-form)
            (list 
              (ops op)
              (if (== 1 (count head-form))
                (infix-form-process 
                  all-ops (first head-form))
                (infix-list-process 
                  all-ops current-ops head-form))
              (if (== 1 (count tail-form))
                (infix-form-process 
                  all-ops (first tail-form))
                (infix-list-process 
                  all-ops rest-ops tail-form)))))
        
        ; Right associative
        (let [[head-form [op & tail-form]] (split-with
                                             (comp not (partial contains? ops))   
                                             list-form)]
          (if (nil? op)
            (recur all-ops rest-ops head-form)
            (list 
              (ops op)
              (if (== 1 (count head-form))
                (infix-form-process 
                  all-ops (first head-form))
                (infix-list-process 
                  all-ops rest-ops head-form))
              (if (== 1 (count tail-form))
                (infix-form-process 
                  all-ops (first tail-form))
                (infix-list-process 
                  all-ops current-ops tail-form)))))

      :default 
        (map 
          (partial infix-form-process all-ops) 
          list-form))))

(defmacro with-infix-ops [& forms]
  `(do
     ~@(map
         (partial infix-form-process standard-ops) 
         forms)))
